home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / tracerp.com / TRACE.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1989-05-12  |  16.7 KB  |  492 lines

  1. {═══════════════════════════════ TRACE.PAS ═══════════════════════════════}
  2. {                 Copyright (c) 1989  Richard W. Prescott                 }
  3. { This Unit contains the assembly code for the basic interrupt routine,   }
  4. { which is installed by calling TraceOn and which is detached by calling  }
  5. { TraceOff or TRelease.  If the interrupt routine is still active upon    }
  6. { normal or abnormal (e.g. Run-Time Error) program termination, it is     }
  7. { detached automatically by the Unit Exit Code.  The original interrupt   }
  8. { vector is stored in the current Code segment to simplify chaining to    }
  9. { the original interrupt routine in TRelease.  The assembly code within   }
  10. { the Procedure THook traps each Interrupt $01 from the subject Code      }
  11. { segment and issues a FAR Jmp via the Pointer variable PascalCode.       }
  12. { Return to the label "Resume" is accomplished via the directive TReturn. }
  13. { PascalCode must be initialized by TraceOn to point to an ordinary (not  }
  14. { interrupt) Procedure which will provide the desired Trace routine.      }
  15. {═════════════════════════════════════════════════════════════════════════}
  16. { This Unit was compiled and assembled using Turbo Pascal Version 5.0     }
  17. { and TP&Asm Version 2.0.  TP&Asm provides an integrated compile-time     }
  18. { assembler within the Turbo development environment (and the command     }
  19. { line compiler TPC), resulting in an ASSEMBLY Development Environment    }
  20. { which is identical to your PASCAL Development Environment.              }
  21. {                                                                         }
  22. {  TP&Asm Version 2.0 is available from me for $49 plus $3 P&H.  Please   }
  23. {             see the file TRACE.DOC for further information.             }
  24. {═════════════════════════════════════════════════════════════════════════}
  25.  
  26. Unit TRACE;
  27. {$D-}
  28.  
  29. interface
  30.  
  31. {- Public Variables -}
  32.  
  33. TYPE
  34.   UserRegs = RECORD
  35.     CASE INTEGER OF
  36.       0: (Ax,Bx,Cx,Dx,Si,Ds,Di,Es,Bp,Ip,Cs,Flags: WORD);
  37.       1: (Al,Ah,Bl,Bh,Cl,Ch,Dl,Dh : BYTE);
  38.   END; {UserRegs}
  39.  
  40. VAR
  41.   TExitSp,UserSP,UserSS: WORD;
  42.   User: ^UserRegs absolute UserSP;
  43.  
  44.  
  45. CONST
  46.   PascalCode: Pointer = Nil;
  47.  
  48.  
  49. {- Public Procedures -}
  50.  
  51. PROCEDURE TraceOn(CodePtr: POINTER); 
  52. PROCEDURE TraceOff;
  53. PROCEDURE TRelease;
  54.  
  55.  
  56. {- Inline Directives -}
  57.  
  58. {════════════════════════════════ TReturn ════════════════════════════════}
  59. { Restore Stack Pointer to its value on entry to the Pascal service       }
  60. { routine, and issue a Far Return.  This technique permits use of TReturn }
  61. { from within nested sub-procedures.  User registers (User^.Ax, etc) may  }
  62. { be inspected but should not be modified.                                }
  63. {════════════════════════════════ TReturn ════════════════════════════════}
  64. PROCEDURE TReturn; {- Inline Directive -}
  65. Assembly
  66.   Mov Sp,TExitSp  ; Restore Stack Pointer
  67.   Retf            ;  .. and return to label "Resume" within THook
  68. END; {- TReturn -}
  69.  
  70. {════════════════════════════════ ReadKey ════════════════════════════════}
  71. { Read keyboard without echo to screen.  (Similar to ReadKey in CRT Unit) }
  72. { Returns the same character that would be returned by CRT Unit ReadKey,  }
  73. { except that ANSI.SYS macros are expanded and Ctrl-C and Ctrl-Break are  }
  74. { treated as characters rather than as user break requests.  (Provided    }
  75. { here so that DEMOTRC.PAS will not require the CRT unit).                }
  76. {════════════════════════════════ ReadKey ════════════════════════════════}
  77. FUNCTION ReadKey:  CHAR; {- Inline Directive -}
  78. Assembly
  79.   Mov Ah,7
  80.   Int 21h
  81. End;
  82.  
  83.  
  84. implementation
  85.  
  86. {- Private variables -}
  87. CONST
  88.   TurboDSS: WORD = 0;
  89.   SigString: STRING[5] = 
  90.         {$IFDEF VER40}   'VER40';   {$ELSE}   'VER50';   {$ENDIF}
  91.  
  92.   TraceErrorAddr: Pointer = Nil;
  93.  
  94.   InstallError0: STRING[45] = 'Cannot nest TraceOn Calls (TRACE was Active)$';
  95.   InstallError1: STRING[45] = 'Trace Code Pointer must point to a Procedure$';
  96.   InstallError2: STRING[44] = 'Trace Procedure must contain a TReturn Call$';
  97.   InstallError3: STRING[45] = 'Trace Code must reside in CS of Subject Code$';
  98.   ReleaseError : STRING[47] = 'Cannot TRelease outside active TRACE procedure$';
  99.   PressAKey: STRING[21] = #13#10'Press any key ... $';
  100.  
  101.   PasTraceExit:  WORD = 0;
  102.   PasTraceEntry: WORD = 0;
  103.  
  104.   TraceFlag  = $0100;      
  105.   TraceClear = $FEFF;
  106.  
  107.  
  108. {════════════════════════════════ CsData ═════════════════════════════════}
  109. { The CSDATA construct is used to store data in the current Code Segment. }
  110. { The original interrupt address Int01Vec must be stored in this Code     }
  111. { Segment to allow Chaining to the original interrupt routine with all of }
  112. { the User Registers intact.  The Word TraceCs is stored in the Code      }
  113. { Segment so that it can be inspected before restoring the Turbo DSeg.    }
  114. { CsData Variables are available throughout the current Unit.             }
  115. {════════════════════════════════ CsData ═════════════════════════════════}
  116. CSDATA
  117.   Int01Vec Dd 0
  118.   Int03Vec Dd 0
  119.   TInt1BEntry Dd 0:01504
  120.   TraceCS Dw 0
  121.   TraceSP Dw 0
  122.   TraceBP Dw 0
  123. END; {CsData}
  124.  
  125.  
  126. {═════════════════════════════════ THook ═════════════════════════════════}
  127. { This is the assembly portion of the interrupt service routine.  First   }
  128. { check that the interrupted code was executing in the designated Trace   }
  129. { CSeg, and if not, issue an immediate return from interrupt.  This will  }
  130. { insure that we may reliably call any Pascal Procedure or Function       }
  131. { (including those which use DOS services) within the Pascal Code of the  }
  132. { Trace routine.  If the CSeg checks out, save registers, restore Ds,     }
  133. { "Push" the Cs:Ip of the label "Resume" onto the stack, and issue an     }
  134. { indirect Jmp to the address stored in the Pointer PascalCode.  Within   }
  135. { the Pascal Trace routine, the interrupted program registers may be      }
  136. { inspected via the User record, eg "InChar := User^.Ax;"                 }
  137. { The Pascal code for the Interrupt Service must end with TReturn.        }
  138. {═════════════════════════════════ THook ═════════════════════════════════}
  139. PROCEDURE THook; Forward;
  140. Internal Hook;
  141. ;- Use INTERNAL to eliminate standard Pascal Startup Code
  142.  
  143. CODE Segment
  144.  
  145. THook PROC NEAR
  146.  
  147.   Push Bp
  148.   Mov Bp,Sp ;- Flags at [Bp+6],  CS at [Bp+4],  IP at [Bp+2],  BP at [Bp+0]
  149.  
  150.   Push Ax
  151.   Mov Ax,[Bp+4]          ; Cs of interrupted program
  152.   Cmp Ax,TraceCS         ; Wake up only for Trace Cs
  153.   jE SaveRegs
  154.   Pop Ax,Bp              ; Else restore Regs and
  155.   Iret                   ;  return to interrupted program
  156.  
  157. SaveRegs:
  158.   Pop Ax
  159.   Push Es,Di,Ds,Si,Dx,Cx,Bx,Ax
  160.  
  161.   Mov Ax,SEG Data
  162.   Mov Ds,Ax              ; Restore Our Ds
  163.  
  164. WakeUp:
  165.   Mov UserSS,Ss          ; Save User Stack Ss:Sp in Our Ds
  166.   Mov UserSP,Sp          ;  (other registers stored on User Stack)
  167.  
  168.   Push Cs                ; "Push" Cs:Ip of label "Resume"
  169.   Call TrapProcessing    ;   onto stack
  170.  
  171. Resume:                  ; Return here from Pascal Trace Routine (TReturn)
  172.   Cmp TraceCs,0          ; If TraceOff called within Pascal Trace routine,
  173.   IF Z And [Bp+6],TraceClear   ;- must clear trace flag here
  174.  
  175.   Pop Ax,Bx,Cx,Dx,Si,Ds,Di,Es,Bp   ;- Restore user registers
  176.   Iret                   ; and return to interrupted program
  177.  
  178. TrapProcessing:
  179.   Mov TExitSp,Sp
  180.   Push TraceBp           ; Push Parent Bp onto stack above a 
  181.   Push Ax                ;  fake return Ip.  Permits access to parent 
  182.                          ;  stack frame if PascalCode is a local Proc
  183.   Jmp PascalCode         ; Jmp via pointer to Pascal Service Routine
  184.  
  185. THook ENDP
  186. CODE ENDS
  187. END {- Internal Hook -}
  188.  
  189.  
  190. {═════════════════════════════ SignalRunError ════════════════════════════}
  191. { On entry Ds:Dx points to a '$'-terminated error message and DWORD PTR   }
  192. { [Bp+2] contains the address of the instruction following an invalid     }
  193. { TraceOn/TRelease Call.  Adjust the segment value to the relative        }
  194. { segment format used for Run-Time errors, save into TraceErrorAddr, and  }
  195. { then issue a Halt(204) to set ExitCode and invoke the exit procedures.  }
  196. {═════════════════════════════ SignalRunError ════════════════════════════}
  197. PROCEDURE SignalRunError;
  198. {$S-} BEGIN {$S+}       {- Don't generate Stack check code -}
  199.  Assembly
  200.   Pop Bp                 ; Restore Bp to its value on entry
  201.   Mov Ah,09              ; Display Error
  202.   Int 21h
  203.   Lea Dx,PressAKey+1
  204.   Mov Ah,09              ; Display "Press any key ..."
  205.   Int 21h
  206.   Xor Ah,Ah
  207.   Int 16h                ; wait for key
  208.  
  209.   Mov Ax,[Bp+2]          ; User Ip following invalid TraceOn/TRelease Call
  210.   Mov W TraceErrorAddr,Ax
  211.  
  212.   Mov Ax,[Bp+4]          ; User Cs of invalid TraceOn/TRelease Call
  213.   Sub Ax,PrefixSeg
  214.   Sub Ax,$10
  215.  Pas {$IFDEF Ver40}
  216.  ;- For the Version 4.0 IDE only, adjust reported error CSeg
  217.   Cmp TurboDSS,0
  218.   jZ NoIDE40
  219.   Mov Es,TurboDSS
  220.   Es Mov Es,[$4428]      ; Point Es to PROGRAM TPU in Memory
  221.   Mov Ax,[Bp+4]          ; User Cs
  222.   Es Sub Ax,[$0022]      ; Subtract Runtime Code Starting Segment
  223.  NoIDE40:
  224.  Pas {$ENDIF}
  225.  
  226.   Mov W TraceErrorAddr+2,Ax
  227.  END; {Assembly}
  228.  
  229.   Halt(204);            {- Signal "Invalid Pointer Operation" -}
  230.   
  231. END; {PROCEDURE SignalRunError}
  232.  
  233.  
  234. {════════════════════════════════ TraceOn ════════════════════════════════}
  235. { Check for valid Trace procedure at CodePtr and if necessary signal a    }
  236. { Run-Time error.  Otherwise, save and install a new Interrupt 01 vecter, }
  237. { set PascalCode := CodePtr, and adjust stack contents to permit use of   }
  238. { Iret to return to the subject code with the hardware trace flag on.     }
  239. {════════════════════════════════ TraceOn ════════════════════════════════}
  240. PROCEDURE TraceOn(CodePtr: POINTER); 
  241. BEGIN
  242.  Assembly
  243.  
  244.   Lea Dx,InstallError0+1
  245.   Cmp TraceCS,0          ; If TRACE is active, don't install
  246.   jNE Error              ; 'Cannot nest TraceOn Calls (TRACE was Active)'
  247.  
  248.   Cld                    ; Scan forward
  249.   Les Di,CodePtr
  250.  
  251.   Mov Al,$89             ; search for 89 EC 5D, standard Proc Exit
  252.   Mov Cx,$FFFF
  253.   Lea Dx,InstallError1+1
  254. L1:
  255.   RepNE ScasB
  256.   jNE Error              ; 'Trace Code Pointer must point to a Procedure'
  257.   Es Cmp W [Di],$5DEC
  258.   jNZ L1
  259.   Mov PasTraceExit,Di    ; Found Trace Proc Exit, save offset
  260.  
  261.   Les Di,CodePtr         ; Restore CodePtr Es:Di
  262.   Mov PasTraceEntry,Di   ; Save offset of Trace Proc Entry
  263.  
  264.   Mov Al,$8B             ; search for 8B 26 XX XX CB  (TReturn)
  265.   Not Cx                 ; expect to find at lower address than Proc Exit
  266.   Lea Dx,InstallError2+1
  267. L2:
  268.   RepNE ScasB
  269.   jNE Error              ; 'Trace Procedure must contain a TReturn Call'
  270.   Es Cmp B [Di],$26
  271.   jNZ L2
  272.   Es Cmp B [Di+3],$CB
  273.   jNZ L2
  274.  
  275.   Les Di,CodePtr         ; Restore CodePtr Es:Di
  276.  
  277.   Mov Ax,Es
  278.   Mov Dx,Cs
  279.   Cmp Ax,Dx
  280.   jE Install             ; Allow Predefined Trace Procedures in this Unit
  281.   Cmp Ax,[Bp+4]
  282.   jE Install             ; Allow Trace Procedures in CS of Subject Code
  283.   Lea Dx,InstallError3+1
  284.   Jmp Error              ; 'Trace Code must reside in CS of Subject Code'
  285.  
  286. Install:
  287.   Mov W PascalCode,Di
  288.   Mov W PascalCode+2,Es  ; PascalCode := CodePtr;
  289.  
  290. ;- Save & Install new interrupt
  291.  
  292.   Mov Ax,03503           ; Get Interrupt into Es:Bx
  293.   Int 021                ;  (Stored in Code Seg to allow Chaining)
  294.   Mov W Int03Vec,Bx
  295.   Mov W Int03Vec+2,Es
  296.  
  297.   Mov Ax,03501           ; Get Interrupt into Es:Bx
  298.   Int 021                ;  (Stored in Code Seg to allow Chaining)
  299.   Mov W Int01Vec,Bx      ; This Assembly Reference will link in CSDATA
  300.   Mov W Int01Vec+2,Es
  301.  
  302.   Mov Ax,02501           ; Set Interrupt to Ds:Dx
  303.   Push Ds,Cs             ; Save DSeg, 
  304.   Pop Ds                 ;  point Ds to CSeg
  305.   Mov Dx,Offset THook    ; This Assembly Reference will Link in THook
  306.   Int 021
  307.   Pop Ds                 ; Restore Ds to DSeg
  308.  
  309.   Pop Bp                 ; Restore Bp Pushed in standard Proc entry
  310.   Mov TraceBp,Bp         ; Save Parent Bp for use in local Trace Procs
  311.   Pop Bx,TraceCS         ; Save Ip in Bx, Set TraceCS
  312.   PushF
  313.   Pop Ax
  314.   Or Ax,TraceFlag        ; Set-up for Iret with TraceFlag enabled
  315.   Push Ax,TraceCS,Bx     ; Flags at [Sp+4], CS at [Sp+2], IP at [Sp+0]
  316.   Iret                   ; NORMAL EXIT
  317.  
  318. Error:                   ; ABNORMAL EXIT
  319.   Call SignalRunError    ; Display Run-Time Error and Halt
  320.  
  321.  END; {Assembly}
  322.  
  323. END; {PROCEDURE TraceOn; }
  324.  
  325.  
  326.  
  327. {═══════════════════════════════ TraceOff ════════════════════════════════}
  328. { Restore the interrupt $01 vector to the value saved during TraceOn,     }
  329. { clear the TraceFlag bit in the current flags, and clear TraceCs.  Can   }
  330. { be called either from the sublect code or from within the Pascal Trace  }
  331. { procedure.  In the latter case the current flags will not affect those  }
  332. { of the subject program, so if TraceCs = 0 after TReturn, the TraceFlag  }
  333. { bit of the subject program flags will be cleared at "Resume:" in THook. }
  334. {═══════════════════════════════ TraceOff ════════════════════════════════}
  335. PROCEDURE TraceOff; 
  336. {$S-} BEGIN {$S+}   {- Don't generate Stack check code -}
  337.  Assembly
  338.  
  339.   PushF
  340.   Pop Ax
  341.   And Ax,TraceClear      ; Clear TraceFlag bit in current flags
  342.   Push Ax
  343.   PopF
  344.  
  345.   Mov Ax,02501           ; Set Interrupt to Ds:Dx
  346.   Push Ds
  347.   Lds Dx,Int01Vec        ; Load Ds:Dx with saved value
  348.   Cmp TraceCS,0          ; If Trace was active,
  349.   IF NE Int 021          ;  restore interrupt vector
  350.   Pop Ds
  351.   Mov TraceCS,0          ; Clear TraceCS
  352.  
  353.  END; {Assembly}
  354. END; {TraceOff}
  355.  
  356.  
  357. {═══════════════════════════════ TRelease ════════════════════════════════}
  358. { Release control to IDE or external debugger.  External debuggers will   }
  359. { trap at the next assembly instruction in the subject module.  The IDE   } 
  360. { debugger will trap the next Pascal instruction in the subject module.   }
  361. { Must be called from within an active Trace routine.                     }
  362. {═══════════════════════════════ TRelease ════════════════════════════════}
  363. PROCEDURE TRelease;
  364. Label Error;
  365. BEGIN
  366.  
  367.  Assembly
  368.  
  369.   Push TraceCS
  370.   Call TraceOff          ; Restore Int01 Vector and clear TraceCs
  371.   Pop Ax                 ; TraceCs value before it was cleared
  372.  
  373.   Lea Dx,ReleaseError+1
  374.   Cmp Ax,[Bp+4]          ; User Cs of TRelease Call
  375.   jNE Error              ; 'Cannot TRelease outside active TRACE procedure'
  376.   Mov Ax,[Bp+2]          ; User Ip of TRelease Call
  377.   Cmp Ax,PasTraceEntry
  378.   jB Error
  379.   Cmp Ax,PasTraceExit
  380.   jA Error
  381.  
  382.   Mov Sp,TExitSp         ; Restore Stack Pointer in
  383.   Add Sp,4               ;  preparation for popping User Regs
  384.  
  385.  END; {Assembly}
  386.  
  387. {$IFDEF VER50}
  388.  
  389.  IF TurboDSS <> 0 THEN Assembly
  390.    
  391.   Mov Es,W Int01Vec+2    ; Turbo InitCS
  392.   Es Mov B[06D8],0
  393.  
  394.   Mov W TInt1BEntry+2,Es
  395.   Mov W TInt1BEntry,01504
  396.   PushF
  397.   Call TInt1BEntry       ; Turbo 5.0 executes this during a user CBreak
  398.  
  399.   Es Mov B[06D9],0
  400.   Xor Ax,Ax
  401.   Mov Es,Ax
  402.   Es And B[0471],07F
  403.  
  404.   Mov W Int01Vec,01537   ; v5 IDE entry point following ^Break
  405.  
  406.  END; {IF TurboDSS <> 0 THEN Assembly}
  407.  
  408. {$ENDIF}
  409.  
  410.  Assembly
  411.  
  412.   Pop Ax,Bx,Cx,Dx,Si,Ds,Di,Es,Bp  ; Restore user registers
  413.  
  414.   Jmp Int01Vec           ; Chain to original Int01 or IDE ^Break entry point
  415.  
  416. Error:
  417.   Call SignalRunError
  418.  
  419.  END; {Assembly}
  420.  
  421. END; {PROCEDURE TRelease}
  422.  
  423.  
  424.  
  425. {═════════════════════════════════ TExit ═════════════════════════════════}
  426. { Unit Exit Procedure to automatically detach interrupt system and force  }
  427. { correct Run-Time error address for invalid TraceOn or TRelease Calls.   }
  428. {═════════════════════════════════ TExit ═════════════════════════════════}
  429. VAR   NextExit: POINTER;
  430. {$F+} PROCEDURE TExit; {$F-}    {- Exit Procedures must use Far Model -}
  431. {$S-} BEGIN {$S+}               {- Don't generate Stack check code -}
  432.  TraceOff;
  433.  ExitProc := NextExit;
  434.  IF ErrorAddr = Nil THEN 
  435.   ErrorAddr:= TraceErrorAddr;   {- Nil if no error -}
  436. END; {TExit}
  437.  
  438.  
  439. {═════════════════════════════ Initialiation ═════════════════════════════}
  440. { Install Unit Exit procedure and automatically detect version 4.0 or     }
  441. { 5.0 Integrated Development Environment.  If found, set TurboDSS to the  }
  442. { IDE's Data/Stack segment.                                               }
  443. {═════════════════════════════ Initialiation ═════════════════════════════}
  444. BEGIN
  445.   NextExit := ExitProc;
  446.   ExitProc := @TExit;       {- Restore Interrupt 01 on Exit -}
  447.  
  448. {- initialization code -}
  449.  Assembly
  450.   Mov TurboDSS,0
  451.   Cld
  452.  Pas {$IFDEF VER40}
  453.   Mov Cx,Cs
  454.   Mov Ax,'yp'
  455.  Pas {$ELSE}
  456.   Mov Cx,PrefixSeg
  457.   Mov Ax,'bA'
  458.  Pas {$ENDIF}
  459.  
  460.  L0:
  461.   jCXZ NoIDE
  462.   Xor Di,Di
  463.   Mov Es,Cx
  464.   ScaSW
  465.   LoopNE L0
  466.  
  467.  CheckSig:
  468.  
  469.  Pas {$IFDEF VER40}
  470.   Mov Es,Cx      ; this effectively decrements Es
  471.   Mov Di,$0FE2   ; v4 DSS offset of #05'VER40'
  472.  Pas {$ELSE}
  473.   Mov Di,$40C8   ; v5 DSS offset of #05'VER50'
  474.  Pas {$ENDIF}
  475.  
  476.   Lea Si,SigString
  477.   Push Cx
  478.   Mov Cx,6
  479.   RepE CmpSB
  480.   Pop Cx
  481.   jNE L0
  482.  
  483.  Found:
  484.   Mov TurboDSS,Es
  485.  
  486.  NoIDE:
  487.  
  488.  END; {Assembly}
  489.  
  490. END.
  491.  
  492.